﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Text;
using SubSonic;
using SubSonic.Utilities;

namespace Medianamik.Core.DAL
{
    /// <summary>
    /// Récupération du ExecuteTransaction de SubSonic adapté à nos besoins
    /// </summary>
    public class Transaction
    {
        private List<QueryCommand> _nonQueryCommands = new List<QueryCommand>();
        private Dictionary<QueryCommand, Action<Object>> _scalarCommands = new Dictionary<QueryCommand,Action<object>>();
        private DataProvider _provider;

        public Transaction(DataProvider pProvider)
        {
            _provider = pProvider;
        }

        public bool HasCommands
        {
            get
            {
                return _nonQueryCommands.Count + _scalarCommands.Count > 0;
            }
        }
        
        public void AddRange(IEnumerable<QueryCommand> pCommands)
        {
            foreach (var command in pCommands)
            {
                Add(command);
            }
        }
        
        public void Add(QueryCommand pCommand)
        {
            _nonQueryCommands.Add(pCommand);
        }
        public void AddScalar(QueryCommand pCommand, Action<Object> pCallback )
        {
            _scalarCommands.Add(pCommand, pCallback);
        }

        public void Execute()
        {
            if (!HasCommands)
            {
                throw new ArgumentOutOfRangeException("commands", "No commands present");
            }
            using (var conn = new AutomaticConnectionScope(_provider))
            {
                if (conn.Connection.State == ConnectionState.Closed)
                {
                    conn.Connection.Open();
                }
                var trans = (SqlTransaction)conn.Connection.BeginTransaction();
                foreach (QueryCommand qry in _nonQueryCommands)
                {
                    var cmd = new SqlCommand(qry.CommandSql, (SqlConnection)conn.Connection, trans);
                    cmd.CommandType = qry.CommandType;
                    AddParams(cmd, qry);
                    try
                    {
                        cmd.ExecuteNonQuery();
                    }
                    catch (SqlException)
                    {
                        trans.Rollback();
                        throw;
                    }
                }

                foreach (QueryCommand qry in _scalarCommands.Keys )
                {
                    var cmd = new SqlCommand(qry.CommandSql, (SqlConnection)conn.Connection, trans);
                    cmd.CommandType = qry.CommandType;
                    AddParams(cmd, qry);
                    try
                    {
                        var value = cmd.ExecuteScalar();
                        var callback = _scalarCommands[qry];
                        if(callback != null)
                        {
                            callback(value);
                        }
                    }
                    catch (SqlException)
                    {
                        trans.Rollback();
                        throw;
                    }
                }
                trans.Commit();
            }

        }

       private static void AddParams(SqlCommand cmd, QueryCommand qry)
        {
            if (qry.Parameters != null)
            {
                foreach (QueryParameter param in qry.Parameters)
                {
                    SqlParameter sqlParam = new SqlParameter(param.ParameterName, Utility.GetSqlDBType(param.DataType));
                    sqlParam.Direction = param.Mode;
                    if ((sqlParam.Direction == ParameterDirection.Output) || (sqlParam.Direction == ParameterDirection.InputOutput))
                    {
                        sqlParam.Size = param.Size;
                    }
                    if (param.Scale.HasValue)
                    {
                        sqlParam.Scale = Convert.ToByte(param.Scale);
                    }
                    if ((param.ParameterValue == null) || Utility.IsMatch(param.ParameterValue.ToString(), "null"))
                    {
                        sqlParam.Value = DBNull.Value;
                    }
                    else if (param.DataType == DbType.Guid)
                    {
                        string paramValue = param.ParameterValue.ToString();
                        if (!string.IsNullOrEmpty(paramValue))
                        {
                            if (!Utility.IsMatch(paramValue, "DEFAULT"))
                            {
                                sqlParam.Value = new Guid(param.ParameterValue.ToString());
                            }
                        }
                        else
                        {
                            sqlParam.Value = DBNull.Value;
                        }
                    }
                    else
                    {
                        sqlParam.Value = param.ParameterValue;
                    }
                    cmd.Parameters.Add(sqlParam);
                }
            }
        }


    }
}
